/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import fs from 'fs';
import path from 'path';

import { Module } from '@ohos/hvigor';

import { findTargetProduct } from '../../common/find-target-product.js';
import { DefaultTargetConst } from '../../const/common-const.js';
import { ApiVersion } from '../../const/sdk-const.js';
import { ModuleType } from '../../enum/module-type-enum.js';
import { ConfigJson } from '../../options/configure/config-json-options.js';
import { buildOptionManager } from '../../project/build-option/build-mode-manager.js';
import { findDuplicateElement, getElementFromArr } from '../../utils/array-util.js';
import { FileUtil } from '../../utils/file-util.js';
import { resModelLoader } from '../../utils/loader/file/res-model-loader.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { LegacyAbilityModelImpl } from '../ability/legacy-ability-model-impl.js';
import { LegacyAbilityModel } from '../ability/legacy-ability-model.js';
import { LegacyFormModelImpl } from '../ability/legacy-form-model-impl.js';
import { LegacyWorkerModelImpl } from '../ability/legacy-worker-model-impl.js';
import { ProjectModel } from '../project/project-model.js';
import { LegacyTargetSourceSetImpl } from '../source-set/legacy-target-source-set-impl.js';
import { LegacyTargetSourceSetModel } from '../source-set/source-set-model.js';
import { CoreModuleModelImpl } from './core-module-model-impl.js';

import ConfigOptObj = ConfigJson.ConfigOptObj;

const defaultTargetName = DefaultTargetConst.DEFAULT_TARGET;
const ohosTestTargetName = DefaultTargetConst.OHOS_TEST_TARGET;

/**
 * Fa模型的模块持久化数据模型,包含模块源码数据,配置数据等
 *
 * @since 2022/1/20
 */
const _log = OhosLogger.getLogger('legacyModuleModel');

export class LegacyModuleModelImpl extends CoreModuleModelImpl {
  private _legacyAbilitiesMap: Map<string, LegacyAbilityModel[]> | undefined;

  constructor(module: Module, parentProject: ProjectModel) {
    super(module, parentProject);
    this.initDefaultTargetSourceSet();
  }

  private static validateSameNameAbility(abilityModels: LegacyAbilityModel[]): void {
    const _log = OhosLogger.getLogger('Ability');
    const dupList = findDuplicateElement(abilityModels.map((model) => model.getName()));
    if (dupList.length > 0) {
      dupList.forEach((abilityModelName) => _log.error(`Duplicate ability name ${abilityModelName}.`));
      _log.printErrorExit('ABILITY_NAME_IS_NOT_UNIQUE', [dupList.join(', ')]);
    }
  }

  initDefaultTargetSourceSet() {
    // 初始化创建default的target sourceSet
    const defaultTargetSourceSet = new LegacyTargetSourceSetImpl(path.resolve(this._sourceRootDir, 'main'));
    this.targetSourceSetMap.set(defaultTargetName, defaultTargetSourceSet);

    const ohosTestTarget = getElementFromArr(this.getProfileOpt().targets, ohosTestTargetName);
    if (ohosTestTarget) {
      const ohosTestTargetSourceSet = new LegacyTargetSourceSetImpl(path.resolve(this._sourceRootDir, ohosTestTargetName));
      this.targetSourceSetMap.set(ohosTestTargetName, ohosTestTargetSourceSet);
    }
  }

  /**
   * 获取需要进行编译的ability信息
   */
  getLegacyAbilities(targetName = defaultTargetName): LegacyAbilityModel[] {
    if (!this._legacyAbilitiesMap) {
      this._legacyAbilitiesMap = new Map<string, LegacyAbilityModel[]>();
      this.initAbilityInfo(defaultTargetName, <LegacyTargetSourceSetModel>this.targetSourceSetMap.get(defaultTargetName));

      if (targetName === ohosTestTargetName) {
        this.initAbilityInfo(ohosTestTargetName, <LegacyTargetSourceSetModel>this.targetSourceSetMap.get(ohosTestTargetName));
      }
    }
    const abilities: LegacyAbilityModel[] | undefined = this._legacyAbilitiesMap.get(targetName);
    return abilities === undefined ? this._legacyAbilitiesMap.get('default')! : abilities;
  }

  getModuleType(targetName = defaultTargetName): ModuleType {
    const legacySourceSet = this.getSourceSetByTargetName(targetName) as LegacyTargetSourceSetImpl;
    const moduleTypeStr: string = legacySourceSet.getLegacyModuleTargetRes().getConfigJsonOpt()?.module?.distro?.moduleType;
    if (moduleTypeStr === undefined) {
      _log.printErrorExit('FAILED_TO_OBTAIL_THE_MODULE_TYPE',[legacySourceSet.getLegacyModuleTargetRes().getJsonPath()]);
    }
    return ModuleType.valueOf(moduleTypeStr);
  }

  getDeviceTypes(): string[] {
    const deviceType = this.getSourceSetByTargetName().getLegacyModuleTargetRes().getConfigJsonOpt().module.deviceType;
    if (deviceType === null || deviceType === undefined) {
      _log.printErrorExit(
        'DEVICE_TYPE_NOT_FOUND_FA',
        [],
        [[this.getSourceSetByTargetName().getLegacyModuleTargetRes().getJsonPath()]]
      );
    }
    return deviceType;
  }

  getSourceSetByTargetName(targetName: string = defaultTargetName): LegacyTargetSourceSetModel {
    if (this.targetSourceSetMap.has(targetName)) {
      return this.targetSourceSetMap.get(targetName)! as LegacyTargetSourceSetModel;
    }
    return this.targetSourceSetMap.get(defaultTargetName)! as LegacyTargetSourceSetModel;
  }

  getJsonObjByTargetName(targetName: string): ConfigOptObj {
    return this.getSourceSetByTargetName(targetName).getLegacyModuleTargetRes().getConfigJsonOpt();
  }

  getJsonPathByTargetName(targetName: string): string {
    return this.getSourceSetByTargetName(targetName).getLegacyModuleTargetRes().getJsonPath();
  }

  isAtomicService(): boolean {
    return (
      this.isInstallFree() ||
      this.getJsonObjByTargetName(defaultTargetName).module?.abilities?.some((ability) => {
        return ability.type === 'service' && ability.formsEnabled;
      }) ||
      false
    );
  }

  isInstallFree(): boolean {
    return this.getJsonObjByTargetName(defaultTargetName).module.distro.installationFree || false;
  }

  private initAbilityInfo(targetName: string, targetSourceSet: LegacyTargetSourceSetModel): void {
    const configJsonPath: string = targetSourceSet.getLegacyModuleTargetRes().getJsonPath();
    if (!fs.existsSync(configJsonPath)) {
      return;
    }
    const configJsonObj: ConfigOptObj = resModelLoader.getConfigJson(configJsonPath);
    const abilityObjs: ConfigJson.AbilitiesObj[] = configJsonObj.module.abilities ? configJsonObj.module.abilities : [];

    const legacyAbilities: LegacyAbilityModel[] = [];
    for (let i = 0; i < abilityObjs.length; i++) {
      legacyAbilities.push(new LegacyAbilityModelImpl(configJsonPath, abilityObjs[i].name));
    }
    LegacyModuleModelImpl.validateSameNameAbility(legacyAbilities);

    // api 8 form
    const jsObjs: ConfigJson.JsObj[] = configJsonObj.module.js ? configJsonObj.module.js : [];
    for (let i = 0; i < jsObjs.length; i++) {
      if (jsObjs[i].type !== 'form') {
        continue;
      }
      legacyAbilities.push(new LegacyFormModelImpl(configJsonPath, jsObjs[i]));
    }

    const productBuildOpt = findTargetProduct(this.getParentProject());
    const api: number = this.getCompileApiMetaByProduct(productBuildOpt.name).version;
    if (targetName !== DefaultTargetConst.OHOS_TEST_TARGET && api > ApiVersion.API_VERSION_8) {
      const workerConfig = FileUtil.convertToAbsolutePaths(
        buildOptionManager.getTargetBuildOption(this.getModule().getName(), targetName)?.sourceOption?.workers ?? [],
        this.getProjectDir(),
      );
      if (workerConfig && workerConfig!.filter((workerFile) => path.extname(workerFile) === '.ts').length > 0) {
        legacyAbilities.push(new LegacyWorkerModelImpl(workerConfig));
      }
    }

    LegacyModuleModelImpl.validateFormUnique(abilityObjs);
    this._legacyAbilitiesMap!.set(targetName, legacyAbilities);
  }

  /**
   * 校验config.json中的forms中name字段为唯一
   *
   * @param legacyAbilitiesObjs
   * @private
   */
  private static validateFormUnique(legacyAbilitiesObjs: ConfigJson.AbilitiesObj[]) {
    const _log = OhosLogger.getLogger('Form');
    const abilityFormNameList: string[] = legacyAbilitiesObjs
      .map((abilityObj) => {
        return abilityObj.forms ? abilityObj.forms.map((form) => form.name) : [];
      })
      .flat();
    const dupAbilityFormName = findDuplicateElement(abilityFormNameList);
    if (dupAbilityFormName.length > 0) {
      dupAbilityFormName.forEach((abilityModelName) => _log.error(`Duplicate ability name ${abilityModelName}.`));
      _log.printErrorExit('FORM_NAME_IS_NOT_UNIQUE', [dupAbilityFormName.join(', ')]);
    }
  }

  getPermission(): object {
    const moduleJsonObj: ConfigOptObj = this.getSourceSetByTargetName().getLegacyModuleTargetRes().getConfigJsonOpt();
    return {
      requestPermissions: moduleJsonObj.module.reqPermissions,
      definePermissions: moduleJsonObj.module.defPermissions,
    };
  }
}
